﻿using System;
using System.Collections.Generic;
using UnityEngine;

namespace MFramework
{
    public class Timer : Singleton<Timer>
    {
        private PriorityQueue<TaskNode> taskQueue;
        //懒删除标记
        private Dictionary<uint, bool> lazyDeleteTag;

        private uint globalTaskID;

        private uint curTime;

        protected override void Init()
        {
            taskQueue = new PriorityQueue<TaskNode>((a, b) => a.excuteTime < b.excuteTime, 128);
            lazyDeleteTag = new Dictionary<uint, bool>(128);

            curTime = (uint)(Time.time * 1000);

            MonoRuntime.Inst.Regist(MonoRuntime.MonoLifecycle.Update, Update);
        }

        /// <param name="task"> 注册的委托任务 </param>
        /// <param name="delay"> 延时多少ms后执行 </param>
        /// <param name="repeatInterval"> 重复执行间隔，0表示不重复执行 </param>
        /// <param name="repeatDuration"> 重复持续时间，-1表示无限重复，实际执行次数 = duration/interval + 1 </param>
        /// <param name="onComplete"> 任务执行完成后的回调 </param>>
        /// <returns></returns>
        public uint Regist(Action task, uint delay, uint repeatInterval = 0, int repeatDuration = -1, Action onComplete = null)
        {
            TaskNode tNode = new TaskNode()
            {
                taskID = ++globalTaskID,
                task = task,
                excuteTime = curTime + delay,
                repeatInterval = repeatInterval,
                onComplete = onComplete
            };
            if(repeatInterval != 0)
            {
                if(repeatDuration == -1)
                {
                    tNode.repeatForever = true;
                }
                else
                {
                    tNode.repeatCount = (uint)repeatDuration / repeatInterval;
                }
            }
            taskQueue.Push(tNode);
            return tNode.taskID;
        }


        public void UnRegist(uint taskID)
        {
            lazyDeleteTag.Add(taskID, true); 
        }

        private void Update()
        {
            curTime = (uint)(Time.time * 1000);

            while (!taskQueue.IsEmpty())
            {
                TaskNode tNode = taskQueue.Top();
                if (lazyDeleteTag.TryGetValue(tNode.taskID, out _))
                {
                    taskQueue.Pop();
                    lazyDeleteTag.Remove(tNode.taskID);
                }
                else if(tNode.excuteTime <= curTime)
                {
                    ExcuteTask(tNode);
                    taskQueue.Pop();
                }
                else
                {
                    break;
                }
            }
        }

        private void ExcuteTask(TaskNode tNode)
        {
            tNode.task?.Invoke();
            if(tNode.repeatForever || tNode.repeatCount-- > 0)
            {
                tNode.excuteTime += tNode.repeatInterval;
                taskQueue.Push(tNode);
            }
            else
            {
                tNode.onComplete?.Invoke();
            }
        }

        internal struct TaskNode
        {
            public uint taskID;
            public Action task;
            public uint excuteTime;
            public uint repeatCount;
            public bool repeatForever;
            public uint repeatInterval;
            public Action onComplete;
        }
    }
}
